

const fs = require('fs')

// createLoaderObject 可以把一个loader从一个绝对路径变成一个loader对象
function createLoaderObject(loader) {
    let normal = require(loader)
    let pitch = normal.pitch
    let raw = normal.raw  //决定loader的参数是字符串还是Buffer
    return {
        path:loader,//存放着此loader的绝对路径
        normal,
        pitch,
        raw, 
        data: {},//每个loader都可以携带一个自定义data对象
        pitchExecuted: false,//此loader的pitch函数是否已经 执行过
        normalExecuted: false//此loader的normal函数是否已经执行过
    }
}

function convertArgs(args, raw) {
    if (raw && !Buffer.isBuffer(args[0])) {
        args[0] = Buffer.from(args[0]);
    } else if (!raw && Buffer.isBuffer(args[0])) {
        args[0] = args[0].toString('utf8');
    }
}
/**
 * 处理loader.pitch
 * @param {*} processOptions 对象，  resourceBuffer=》存储源文件的buffer，readResource=》读写文件的方法
 * @param {*} loaderContext 上下文
 * @param {*} args resourceBuffer
 * @param {*} pitchingCallback 回调
 */
function iterateNormalLoaders(processOptions, loaderContext, args, pitchingCallback) {
    if (loaderContext.loaderIndex < 0) {
        return pitchingCallback(null, ...args);
    }
    let currentLoader = loaderContext.loaders[loaderContext.loaderIndex];
    if (currentLoader.normalExecuted) { //如果执行过了
        loaderContext.loaderIndex--;
        return iterateNormalLoaders(processOptions, loaderContext, args, pitchingCallback);
    }
    let fn = currentLoader.normal;
    currentLoader.normalExecuted = true;
    // 判断返回是buffer还是string
    convertArgs(args, currentLoader.raw);
    runSyncOrAsync(fn, loaderContext, args, (err, ...returnArgs) => {
        if (err) return pitchingCallback(err);
        return iterateNormalLoaders(processOptions, loaderContext, returnArgs, pitchingCallback);
    });
}
/**
 * 处理文件资源
 * @param {*} processOptions 对象，  resourceBuffer=》存储源文件的buffer，readResource=》读写文件的方法
 * @param {*} loaderContext 上下文
 * @param {*} pitchingCallback 回调
 */
function processResource(processOptions,loaderContext,pitchingCallback) {
    processOptions.readResource(loaderContext.resource,(err,resourceBuffer)=>{
        processOptions.resourceBuffer = resourceBuffer
        loaderContext.loaderIndex--;//定位到最后一个loader
        iterateNormalLoaders(processOptions, loaderContext, [resourceBuffer], pitchingCallback);
    })

}

/**
 * 处理loader.pitch
 * @param {*} processOptions 对象，  resourceBuffer=》存储源文件的buffer，readResource=》读写文件的方法
 * @param {*} loaderContext 上下文
 * @param {*} pitchingCallback 回调
 */
function iteratePitchingLoaders(processOptions,loaderContext,pitchingCallback) {
    //如果index>length ，说明所有的loader的pitch都已经执行完成
    if (loaderContext.loaderIndex >= loaderContext.loaders.length) {
        // 处理文件资源
        return processResource(processOptions, loaderContext, pitchingCallback);
    }
    // 获取当前loader
    let currentLoader = loaderContext.loaders[loaderContext.loaderIndex];
    if(currentLoader.pitchExecuted){
        loaderContext.loaderIndex++ //如果当前pitch已经执行过了，就让索引+1
        return iteratePitchingLoaders(processOptions, loaderContext, pitchingCallback);
    }
    let fn = currentLoader.pitch
    currentLoader.pitchExecuted = true //表示当前的loader的pitch已经处理过
    if(!fn){ //如果不存在pitch，继续执行iteratePitchingLoaders
        return iteratePitchingLoaders(processOptions,loaderContext,pitchingCallback)
    }
    //以同步或者异步的方式执行fn
    runSyncOrAsync(fn, loaderContext, [
        loaderContext.remainingRequest, loaderContext.previousRequest, loaderContext.data
    ], (err, ...args) => {
        //如果有返回值，索引减少1，并执行前一个loader的normal
        if (args.length > 0 && args.some(item => item)) {
            loaderContext.loaderIndex--;//索引减少1
            iterateNormalLoaders(processOptions, loaderContext, args, pitchingCallback);
        } else {
            return iteratePitchingLoaders(processOptions, loaderContext, pitchingCallback);
        }
    });
}
/**
 * 以同步或者异步的方式执行loader的pitch
 * @param {*} fn 当前loader的pitch
 * @param {*} loaderContext 上下文
 * @param {*} args [remainingRequest,previousRequest,data]
 * @param {*} runCallback 
 */
function runSyncOrAsync(fn, loaderContext, args, runCallback) {
    let isSync = true;//这个是个标志符，用来标志fn的执行是同步还是异步，默认是同步
    loaderContext.callback = (...args)=>{
        runCallback(null,...args)
    }
    loaderContext.async = ()=>{
        isSync = false //从同步改为异步
        return loaderContext.callback
    }
    //在执行pitch方法的时候 ，this指向loaderContext
    let result = fn.apply(loaderContext,args)
    if(isSync){ //如果是同步的执行的话，会立刻向下执行下一个loader
        runCallback(null,result)
    }//如果是异步的话，就什么都不需要做
}
function runLoaders(options,finalCallback) {
    /**
     * resource 要加载的资源  src\index.js
     * loaders  需要加载的loader
     * context  上下文，保存一些状态和值 
     * readResource 读写文件的方法
     */
    let { resource,loaders=[],context={},readResource=fs.readFile } = options 
    let loaderObjects = loaders.map(createLoaderObject); 
    let loaderContext = context;
    loaderContext.resource = resource;//要加载的资源
    loaderContext.readResource = readResource;//读取资源的方法
    loaderContext.loaders = loaderObjects;//所有的loader对象
    loaderContext.loaderIndex = 0;//当前正在执行的loader索引
    loaderContext.callback = null;//回调
    loaderContext.async = null;//把loader的执行从同步变成异步，当次loader函数执行完成以后，不会立即执行下一个loader，而是会等待异步执行完毕再执行下一个loader
    //聚合loader，并且加上resouce ===>   （loader1!loader2!loader3!index.js）
    Object.defineProperty(loaderContext,'request',{
        get(){
            //loader1!loader2!loader3!index.js
            return loaderContext.loaders.map(loader=>loader.path).concat(loaderContext.resource).join('!')
        }
    })
    //获取从下一个loader开始一直到结束的loader ，加上要加载的资源
    Object.defineProperty(loaderContext,'remainingRequest',{
        get(){
            return loaderContext.loaders.slice(loaderContext.loaderIndex + 1).map(loader => loader.path).concat(loaderContext.resource).join('!');
        }
    })
    //从当前的loader开始一直到结束 ，加上要加载的资源
    Object.defineProperty(loaderContext, 'currentRequest', {
        get() {
            return loaderContext.loaders.slice(loaderContext.loaderIndex).map(loader => loader.path).concat(loaderContext.resource).join('!');
        }
    });
    //从第一个到当前的loader的前一个
    Object.defineProperty(loaderContext, 'previousRequest', {
        get() {
            return loaderContext.loaders.slice(0, loaderContext.loaderIndex).map(loader => loader.path).join('!');
        }
    });
    // 当前loader中的自定义data
    Object.defineProperty(loaderContext, 'data', {
        get() {
            return loaderContext.loaders[loaderContext.loaderIndex]?.data;
        }
    });
    let processOptions = {
        resourceBuffer: null,//将要存放读到的原始文件的原始文件 index.js的内容 Buffer
        readResource
    }
    debugger
    iteratePitchingLoaders(processOptions,loaderContext,(err,result)=>{
        finalCallback(err,{
            result,
            resourceBuffer:processOptions.resourceBuffer
        })
    })

}
// module.exports = runLoaders
exports.runLoaders = runLoaders